]> Pileus Git - ~andy/gtk/blob - gtk/gtktextbufferserialize.c
don't g_free() the members of info->tag_stack since they are a) gobjects
[~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 #include "gtkintl.h"
35 #include "gtkalias.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   GSList *active_tags;
438
439   g_string_append (context->text_str, "<text>");
440
441   iter = context->start;
442   tag_list = NULL;
443   active_tags = NULL;
444
445   do
446     {
447       GList *added, *removed;
448       GList *tmp;
449       gchar *tmp_text, *escaped_text;
450
451       new_tag_list = gtk_text_iter_get_tags (&iter);
452       find_list_delta (tag_list, new_tag_list, &added, &removed);
453
454       /* Handle removed tags */
455       for (tmp = removed; tmp; tmp = tmp->next)
456         {
457           GtkTextTag *tag = tmp->data;
458
459           /* Only close the tag if we didn't close it before (by using
460            * the stack logic in the while() loop below)
461            */
462           if (g_slist_find (active_tags, tag))
463             {
464               g_string_append (context->text_str, "</apply_tag>");
465
466               /* Drop all tags that were opened after this one (which are
467                * above this on in the stack)
468                */
469               while (active_tags->data != tag)
470                 {
471                   added = g_list_prepend (added, active_tags->data);
472                   active_tags = g_slist_remove (active_tags, active_tags->data);
473                   g_string_append_printf (context->text_str, "</apply_tag>");
474                 }
475
476               active_tags = g_slist_remove (active_tags, active_tags->data);
477             }
478         }
479
480       /* Handle added tags */
481       for (tmp = added; tmp; tmp = tmp->next)
482         {
483           GtkTextTag *tag = tmp->data;
484           gchar *tag_name;
485
486           /* Add it to the tag hash table */
487           g_hash_table_insert (context->tags, tag, tag);
488
489           if (tag->name)
490             {
491               tag_name = g_markup_escape_text (tag->name, -1);
492
493               g_string_append_printf (context->text_str, "<apply_tag name=\"%s\">", tag_name);
494               g_free (tag_name);
495             }
496           else
497             {
498               gpointer tag_id;
499
500               /* We've got an anonymous tag, find out if it's been
501                  used before */
502               if (!g_hash_table_lookup_extended (context->tag_id_tags, tag, NULL, &tag_id))
503                 {
504                   tag_id = GINT_TO_POINTER (context->tag_id++);
505
506                   g_hash_table_insert (context->tag_id_tags, tag, tag_id);
507                 }
508
509               g_string_append_printf (context->text_str, "<apply_tag id=\"%d\">", GPOINTER_TO_INT (tag_id));
510             }
511
512           active_tags = g_slist_prepend (active_tags, tag);
513         }
514
515       g_slist_free (tag_list);
516       tag_list = new_tag_list;
517
518       g_list_free (added);
519       g_list_free (removed);
520
521       old_iter = iter;
522
523       /* Now try to go to either the next tag toggle, or if a pixbuf appears */
524       while (TRUE)
525         {
526           gunichar ch = gtk_text_iter_get_char (&iter);
527
528           if (ch == 0xFFFC)
529             {
530               GdkPixbuf *pixbuf = gtk_text_iter_get_pixbuf (&iter);
531
532               if (pixbuf)
533                 {
534                   /* Append the text before the pixbuf */
535                   tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
536                   escaped_text = g_markup_escape_text (tmp_text, -1);
537                   g_free (tmp_text);
538
539                   /* Forward so we don't get the 0xfffc char */
540                   gtk_text_iter_forward_char (&iter);
541                   old_iter = iter;
542
543                   g_string_append (context->text_str, escaped_text);
544                   g_free (escaped_text);
545
546                   g_string_append_printf (context->text_str, "<pixbuf index=\"%d\" />", context->n_pixbufs);
547
548                   context->n_pixbufs++;
549                   context->pixbufs = g_list_prepend (context->pixbufs, pixbuf);
550                 }
551             }
552           else if (ch == 0)
553             {
554                 break;
555             }
556           else
557             gtk_text_iter_forward_char (&iter);
558
559           if (gtk_text_iter_toggles_tag (&iter, NULL))
560             break;
561         }
562
563       /* We might have moved too far */
564       if (gtk_text_iter_compare (&iter, &context->end) > 0)
565         iter = context->end;
566
567       /* Append the text */
568       tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
569       escaped_text = g_markup_escape_text (tmp_text, -1);
570       g_free (tmp_text);
571
572       g_string_append (context->text_str, escaped_text);
573       g_free (escaped_text);
574     }
575   while (!gtk_text_iter_equal (&iter, &context->end));
576
577   /* Close any open tags */
578   for (tag_list = active_tags; tag_list; tag_list = tag_list->next)
579     g_string_append (context->text_str, "</apply_tag>");
580
581   g_slist_free (active_tags);
582   g_string_append (context->text_str, "</text>\n</text_view_markup>\n");
583 }
584
585 static void
586 serialize_pixbufs (SerializationContext *context,
587                    GString              *text)
588 {
589   GList *list;
590
591   for (list = context->pixbufs; list != NULL; list = list->next)
592     {
593       GdkPixbuf *pixbuf = list->data;
594       GdkPixdata pixdata;
595       guint8 *tmp;
596       guint len;
597
598       gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE);
599       tmp = gdk_pixdata_serialize (&pixdata, &len);
600
601       serialize_section_header (text, "GTKTEXTBUFFERPIXBDATA-0001", len);
602       g_string_append_len (text, (gchar *) tmp, len);
603       g_free (tmp);
604     }
605 }
606
607 guint8 *
608 _gtk_text_buffer_serialize_rich_text (GtkTextBuffer     *register_buffer,
609                                       GtkTextBuffer     *content_buffer,
610                                       const GtkTextIter *start,
611                                       const GtkTextIter *end,
612                                       gsize             *length,
613                                       gpointer           user_data)
614 {
615   SerializationContext context;
616   GString *text;
617
618   context.tags = g_hash_table_new (NULL, NULL);
619   context.text_str = g_string_new (NULL);
620   context.tag_table_str = g_string_new (NULL);
621   context.start = *start;
622   context.end = *end;
623   context.n_pixbufs = 0;
624   context.pixbufs = NULL;
625   context.tag_id = 0;
626   context.tag_id_tags = g_hash_table_new (NULL, NULL);
627
628   /* We need to serialize the text before the tag table so we know
629      what tags are used */
630   serialize_text (content_buffer, &context);
631   serialize_tags (&context);
632
633   text = g_string_new (NULL);
634   serialize_section_header (text, "GTKTEXTBUFFERCONTENTS-0001",
635                             context.tag_table_str->len + context.text_str->len);
636
637   g_string_append_len (text, context.tag_table_str->str, context.tag_table_str->len);
638   g_string_append_len (text, context.text_str->str, context.text_str->len);
639
640   context.pixbufs = g_list_reverse (context.pixbufs);
641   serialize_pixbufs (&context, text);
642
643   g_hash_table_destroy (context.tags);
644   g_list_free (context.pixbufs);
645   g_string_free (context.text_str, TRUE);
646   g_string_free (context.tag_table_str, TRUE);
647   g_hash_table_destroy (context.tag_id_tags);
648
649   *length = text->len;
650
651   return (guint8 *) g_string_free (text, FALSE);
652 }
653
654 typedef enum
655 {
656   STATE_START,
657   STATE_TEXT_VIEW_MARKUP,
658   STATE_TAGS,
659   STATE_TAG,
660   STATE_ATTR,
661   STATE_TEXT,
662   STATE_APPLY_TAG,
663   STATE_PIXBUF
664 } ParseState;
665
666 typedef struct
667 {
668   gchar *text;
669   GdkPixbuf *pixbuf;
670   GSList *tags;
671 } TextSpan;
672
673 typedef struct
674 {
675   GtkTextTag *tag;
676   gint prio;
677 } TextTagPrio;
678
679 typedef struct
680 {
681   GSList *states;
682
683   GList *headers;
684
685   GtkTextBuffer *buffer;
686
687   /* Tags that are defined in <tag> elements */
688   GHashTable *defined_tags;
689
690   /* Tags that are anonymous */
691   GHashTable *anonymous_tags;
692
693   /* Tag name substitutions */
694   GHashTable *substitutions;
695
696   /* Current tag */
697   GtkTextTag *current_tag;
698
699   /* Priority of current tag */
700   gint current_tag_prio;
701
702   /* Id of current tag */
703   gint current_tag_id;
704
705   /* Tags and their priorities */
706   GList *tag_priorities;
707
708   GSList *tag_stack;
709
710   GList *spans;
711
712   gboolean create_tags;
713
714   gboolean parsed_text;
715   gboolean parsed_tags;
716 } ParseInfo;
717
718 static void
719 set_error (GError              **err,
720            GMarkupParseContext  *context,
721            int                   error_domain,
722            int                   error_code,
723            const char           *format,
724            ...)
725 {
726   int line, ch;
727   va_list args;
728   char *str;
729
730   g_markup_parse_context_get_position (context, &line, &ch);
731
732   va_start (args, format);
733   str = g_strdup_vprintf (format, args);
734   va_end (args);
735
736   g_set_error (err, error_domain, error_code,
737                ("Line %d character %d: %s"),
738                line, ch, str);
739
740   g_free (str);
741 }
742
743 static void
744 push_state (ParseInfo  *info,
745             ParseState  state)
746 {
747   info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
748 }
749
750 static void
751 pop_state (ParseInfo *info)
752 {
753   g_return_if_fail (info->states != NULL);
754
755   info->states = g_slist_remove (info->states, info->states->data);
756 }
757
758 static ParseState
759 peek_state (ParseInfo *info)
760 {
761   g_return_val_if_fail (info->states != NULL, STATE_START);
762
763   return GPOINTER_TO_INT (info->states->data);
764 }
765
766 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
767
768
769 static gboolean
770 check_id_or_name (GMarkupParseContext  *context,
771                   const gchar          *element_name,
772                   const gchar         **attribute_names,
773                   const gchar         **attribute_values,
774                   gint                 *id,
775                   const gchar         **name,
776                   GError              **error)
777 {
778   gboolean has_id = FALSE;
779   gboolean has_name = FALSE;
780   int i;
781
782   *id = 0;
783   *name = NULL;
784
785   for (i = 0; attribute_names[i] != NULL; i++)
786     {
787       if (strcmp (attribute_names[i], "name") == 0)
788         {
789           *name = attribute_values[i];
790
791           if (has_id)
792             {
793               set_error (error, context,
794                          G_MARKUP_ERROR,
795                          G_MARKUP_ERROR_PARSE,
796                          _("Both \"id\" and \"name\" were found on the <%s> element"),
797                          element_name);
798               return FALSE;
799             }
800
801           if (has_name)
802             {
803               set_error (error, context,
804                          G_MARKUP_ERROR,
805                          G_MARKUP_ERROR_PARSE,
806                          _("The attribute \"%s\" was found twice on the <%s> element"),
807                          "name", element_name);
808               return FALSE;
809             }
810
811           has_name = TRUE;
812         }
813       else if (strcmp (attribute_names[i], "id") == 0)
814         {
815           gchar *tmp;
816
817           if (has_name)
818             {
819               set_error (error, context,
820                          G_MARKUP_ERROR,
821                          G_MARKUP_ERROR_PARSE,
822                          _("Both \"id\" and \"name\" were found on the <%s> element"),
823                          element_name);
824               return FALSE;
825             }
826
827           if (has_id)
828             {
829               set_error (error, context,
830                          G_MARKUP_ERROR,
831                          G_MARKUP_ERROR_PARSE,
832                          _("The attribute \"%s\" was found twice on the <%s> element"),
833                          "id", element_name);
834               return FALSE;
835             }
836
837           has_id = TRUE;
838
839           /* Try parsing the integer */
840           *id = strtol (attribute_values[i], &tmp, 10);
841
842           if (tmp == NULL || tmp == attribute_values[i])
843             {
844               set_error (error, context,
845                          G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
846                          _("<%s> element has invalid id \"%s\""), attribute_values[i]);
847               return FALSE;
848             }
849         }
850     }
851
852   if (!has_id && !has_name)
853     {
854       set_error (error, context,
855                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
856                  _("<%s> element has neither a \"name\" nor an \"id\" attribute"), element_name);
857       return FALSE;
858     }
859
860   return TRUE;
861 }
862
863 typedef struct
864 {
865   const char  *name;
866   const char **retloc;
867 } LocateAttr;
868
869 static gboolean
870 locate_attributes (GMarkupParseContext  *context,
871                    const char           *element_name,
872                    const char          **attribute_names,
873                    const char          **attribute_values,
874                    gboolean              allow_unknown_attrs,
875                    GError              **error,
876                    const char           *first_attribute_name,
877                    const char          **first_attribute_retloc,
878                    ...)
879 {
880   va_list args;
881   const char *name;
882   const char **retloc;
883   int n_attrs;
884 #define MAX_ATTRS 24
885   LocateAttr attrs[MAX_ATTRS];
886   gboolean retval;
887   int i;
888
889   g_return_val_if_fail (first_attribute_name != NULL, FALSE);
890   g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
891
892   retval = TRUE;
893
894   n_attrs = 1;
895   attrs[0].name = first_attribute_name;
896   attrs[0].retloc = first_attribute_retloc;
897   *first_attribute_retloc = NULL;
898
899   va_start (args, first_attribute_retloc);
900
901   name = va_arg (args, const char*);
902   retloc = va_arg (args, const char**);
903
904   while (name != NULL)
905     {
906       g_return_val_if_fail (retloc != NULL, FALSE);
907
908       g_assert (n_attrs < MAX_ATTRS);
909
910       attrs[n_attrs].name = name;
911       attrs[n_attrs].retloc = retloc;
912       n_attrs += 1;
913       *retloc = NULL;
914
915       name = va_arg (args, const char*);
916       retloc = va_arg (args, const char**);
917     }
918
919   va_end (args);
920
921   if (!retval)
922     return retval;
923
924   i = 0;
925   while (attribute_names[i])
926     {
927       int j;
928       gboolean found;
929
930       found = FALSE;
931       j = 0;
932       while (j < n_attrs)
933         {
934           if (strcmp (attrs[j].name, attribute_names[i]) == 0)
935             {
936               retloc = attrs[j].retloc;
937
938               if (*retloc != NULL)
939                 {
940                   set_error (error, context,
941                              G_MARKUP_ERROR,
942                              G_MARKUP_ERROR_PARSE,
943                              _("Attribute \"%s\" repeated twice on the same <%s> element"),
944                              attrs[j].name, element_name);
945                   retval = FALSE;
946                   goto out;
947                 }
948
949               *retloc = attribute_values[i];
950               found = TRUE;
951             }
952
953           ++j;
954         }
955
956       if (!found && !allow_unknown_attrs)
957         {
958           set_error (error, context,
959                      G_MARKUP_ERROR,
960                      G_MARKUP_ERROR_PARSE,
961                      _("Attribute \"%s\" is invalid on <%s> element in this context"),
962                      attribute_names[i], element_name);
963           retval = FALSE;
964           goto out;
965         }
966
967       ++i;
968     }
969
970  out:
971   return retval;
972 }
973
974 static gboolean
975 check_no_attributes (GMarkupParseContext  *context,
976                      const char           *element_name,
977                      const char          **attribute_names,
978                      const char          **attribute_values,
979                      GError              **error)
980 {
981   if (attribute_names[0] != NULL)
982     {
983       set_error (error, context,
984                  G_MARKUP_ERROR,
985                  G_MARKUP_ERROR_PARSE,
986                  _("Attribute \"%s\" is invalid on <%s> element in this context"),
987                  attribute_names[0], element_name);
988       return FALSE;
989     }
990
991   return TRUE;
992 }
993
994 static GtkTextTag *
995 tag_exists (GMarkupParseContext *context,
996             const gchar         *name,
997             gint                 id,
998             ParseInfo           *info,
999             GError             **error)
1000 {
1001   const gchar *real_name;
1002
1003   if (info->create_tags)
1004     {
1005       /* If we have an anonymous tag, just return it directly */
1006       if (!name)
1007         return g_hash_table_lookup (info->anonymous_tags,
1008                                     GINT_TO_POINTER (id));
1009
1010       /* First, try the substitutions */
1011       real_name = g_hash_table_lookup (info->substitutions, name);
1012
1013       if (real_name)
1014         return gtk_text_tag_table_lookup (info->buffer->tag_table, real_name);
1015
1016       /* Next, try the list of defined tags */
1017       if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1018         return gtk_text_tag_table_lookup (info->buffer->tag_table, name);
1019
1020       set_error (error, context,
1021                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1022                  _("Tag \"%s\" has not been defined."), name);
1023
1024       return NULL;
1025     }
1026   else
1027     {
1028       GtkTextTag *tag;
1029
1030       if (!name)
1031         {
1032           set_error (error, context,
1033                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1034                      _("Anonymous tag found and tags can not be created."));
1035           return NULL;
1036         }
1037
1038       tag = gtk_text_tag_table_lookup (info->buffer->tag_table, name);
1039
1040       if (tag)
1041         return tag;
1042
1043       set_error (error, context,
1044                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1045                  _("Tag \"%s\" does not exist in buffer and tags can not be created."), name);
1046
1047       return NULL;
1048     }
1049 }
1050
1051 typedef struct
1052 {
1053   const gchar *id;
1054   gint length;
1055   const gchar *start;
1056 } Header;
1057
1058 static GdkPixbuf *
1059 get_pixbuf_from_headers (GList   *headers,
1060                          int      id,
1061                          GError **error)
1062 {
1063   Header *header;
1064   GdkPixdata pixdata;
1065   GdkPixbuf *pixbuf;
1066
1067   header = g_list_nth_data (headers, id);
1068
1069   if (!header)
1070     return NULL;
1071
1072   if (!gdk_pixdata_deserialize (&pixdata, header->length,
1073                                 (const guint8 *) header->start, error))
1074     return NULL;
1075
1076   pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE, error);
1077
1078   return pixbuf;
1079 }
1080
1081 static void
1082 parse_apply_tag_element (GMarkupParseContext  *context,
1083                          const gchar          *element_name,
1084                          const gchar         **attribute_names,
1085                          const gchar         **attribute_values,
1086                          ParseInfo            *info,
1087                          GError              **error)
1088 {
1089   const gchar *name, *priority;
1090   gint id;
1091   GtkTextTag *tag;
1092
1093   g_assert (peek_state (info) == STATE_TEXT ||
1094             peek_state (info) == STATE_APPLY_TAG);
1095
1096   if (ELEMENT_IS ("apply_tag"))
1097     {
1098       if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1099                               "priority", &priority, NULL))
1100         return;
1101
1102       if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1103                              &id, &name, error))
1104         return;
1105
1106
1107       tag = tag_exists (context, name, id, info, error);
1108
1109       if (!tag)
1110         return;
1111
1112       info->tag_stack = g_slist_prepend (info->tag_stack, tag);
1113
1114       push_state (info, STATE_APPLY_TAG);
1115     }
1116   else if (ELEMENT_IS ("pixbuf"))
1117     {
1118       int int_id;
1119       GdkPixbuf *pixbuf;
1120       TextSpan *span;
1121       const gchar *pixbuf_id;
1122
1123       if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1124                               "index", &pixbuf_id, NULL))
1125         return;
1126
1127       int_id = atoi (pixbuf_id);
1128       pixbuf = get_pixbuf_from_headers (info->headers, int_id, error);
1129
1130       span = g_new0 (TextSpan, 1);
1131       span->pixbuf = pixbuf;
1132       span->tags = NULL;
1133
1134       info->spans = g_list_prepend (info->spans, span);
1135
1136       if (!pixbuf)
1137         return;
1138
1139       push_state (info, STATE_PIXBUF);
1140     }
1141   else
1142     set_error (error, context,
1143                G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1144                _("Element <%s> is not allowed below <%s>"),
1145                element_name, peek_state(info) == STATE_TEXT ? "text" : "apply_tag");
1146 }
1147
1148 static void
1149 parse_attr_element (GMarkupParseContext  *context,
1150                     const gchar          *element_name,
1151                     const gchar         **attribute_names,
1152                     const gchar         **attribute_values,
1153                     ParseInfo            *info,
1154                     GError              **error)
1155 {
1156   const gchar *name, *type, *value;
1157   GType gtype;
1158   GValue gvalue = { 0 };
1159   GParamSpec *pspec;
1160
1161   g_assert (peek_state (info) == STATE_TAG);
1162
1163   if (ELEMENT_IS ("attr"))
1164     {
1165       if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1166                               "name", &name, "type", &type, "value", &value, NULL))
1167         return;
1168
1169       gtype = g_type_from_name (type);
1170
1171       if (gtype == G_TYPE_INVALID)
1172         {
1173           set_error (error, context,
1174                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1175                      _("\"%s\" is not a valid attribute type"), type);
1176           return;
1177         }
1178
1179       if (!(pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info->current_tag), name)))
1180         {
1181           set_error (error, context,
1182                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1183                      _("\"%s\" is not a valid attribute name"), name);
1184           return;
1185         }
1186
1187       g_value_init (&gvalue, gtype);
1188
1189       if (!deserialize_value (value, &gvalue))
1190         {
1191           set_error (error, context,
1192                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1193                      _("\"%s\" could not be converted to a value of type \"%s\" for attribute \"%s\""),
1194                      value, type, name);
1195           return;
1196         }
1197
1198       if (g_param_value_validate (pspec, &gvalue))
1199         {
1200           set_error (error, context,
1201                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1202                      _("\"%s\" is not a valid value for attribute \"%s\""),
1203                      value, name);
1204           g_value_unset (&gvalue);
1205           return;
1206         }
1207
1208       g_object_set_property (G_OBJECT (info->current_tag),
1209                              name, &gvalue);
1210
1211       g_value_unset (&gvalue);
1212
1213       push_state (info, STATE_ATTR);
1214     }
1215   else
1216     {
1217       set_error (error, context,
1218                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1219                  _("Element <%s> is not allowed below <%s>"),
1220                  element_name, "tag");
1221     }
1222 }
1223
1224
1225 static gchar *
1226 get_tag_name (ParseInfo   *info,
1227               const gchar *tag_name)
1228 {
1229   gchar *name;
1230   gint i;
1231
1232   name = g_strdup (tag_name);
1233
1234   if (!info->create_tags)
1235     return name;
1236
1237   i = 0;
1238
1239   while (gtk_text_tag_table_lookup (info->buffer->tag_table, name) != NULL)
1240     {
1241       g_free (name);
1242       name = g_strdup_printf ("%s-%d", tag_name, ++i);
1243     }
1244
1245   if (i != 0)
1246     {
1247       g_hash_table_insert (info->substitutions, g_strdup (tag_name), g_strdup (name));
1248     }
1249
1250   return name;
1251 }
1252
1253 static void
1254 parse_tag_element (GMarkupParseContext  *context,
1255                    const gchar          *element_name,
1256                    const gchar         **attribute_names,
1257                    const gchar         **attribute_values,
1258                    ParseInfo            *info,
1259                    GError              **error)
1260 {
1261   const gchar *name, *priority;
1262   gchar *tag_name;
1263   gint id;
1264   gint prio;
1265   gchar *tmp;
1266
1267   g_assert (peek_state (info) == STATE_TAGS);
1268
1269   if (ELEMENT_IS ("tag"))
1270     {
1271       if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1272                               "priority", &priority, NULL))
1273         return;
1274
1275       if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1276                              &id, &name, error))
1277         return;
1278
1279       if (name)
1280         {
1281           if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1282             {
1283               set_error (error, context,
1284                          G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1285                          _("Tag \"%s\" already defined"), name);
1286               return;
1287             }
1288         }
1289
1290       prio = strtol (priority, &tmp, 10);
1291
1292       if (tmp == NULL || tmp == priority)
1293         {
1294           set_error (error, context,
1295                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1296                      _("Tag \"%s\" has invalid priority \"%s\""), name, priority);
1297           return;
1298         }
1299
1300       if (name)
1301         {
1302           tag_name = get_tag_name (info, name);
1303           info->current_tag = gtk_text_tag_new (tag_name);
1304           g_free (tag_name);
1305         }
1306       else
1307         {
1308           info->current_tag = gtk_text_tag_new (NULL);
1309           info->current_tag_id = id;
1310         }
1311
1312       info->current_tag_prio = prio;
1313
1314       push_state (info, STATE_TAG);
1315     }
1316   else
1317     {
1318       set_error (error, context,
1319                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1320                  _("Element <%s> is not allowed below <%s>"),
1321                  element_name, "tags");
1322     }
1323 }
1324
1325 static void
1326 start_element_handler (GMarkupParseContext  *context,
1327                        const gchar          *element_name,
1328                        const gchar         **attribute_names,
1329                        const gchar         **attribute_values,
1330                        gpointer              user_data,
1331                        GError              **error)
1332 {
1333   ParseInfo *info = user_data;
1334
1335   switch (peek_state (info))
1336     {
1337     case STATE_START:
1338       if (ELEMENT_IS ("text_view_markup"))
1339         {
1340           if (!check_no_attributes (context, element_name,
1341                                     attribute_names, attribute_values, error))
1342             return;
1343
1344           push_state (info, STATE_TEXT_VIEW_MARKUP);
1345           break;
1346         }
1347       else
1348         set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1349                    _("Outermost element in text must be <text_view_markup> not <%s>"),
1350                    element_name);
1351       break;
1352     case STATE_TEXT_VIEW_MARKUP:
1353       if (ELEMENT_IS ("tags"))
1354         {
1355           if (info->parsed_tags)
1356             {
1357               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1358                          _("A <%s> element has already been specified"), "tags");
1359               return;
1360             }
1361
1362           if (!check_no_attributes (context, element_name,
1363                                     attribute_names, attribute_values, error))
1364             return;
1365
1366           push_state (info, STATE_TAGS);
1367           break;
1368         }
1369       else if (ELEMENT_IS ("text"))
1370         {
1371           if (info->parsed_text)
1372             {
1373               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1374                          _("A <%s> element has already been specified"), "text");
1375               return;
1376             }
1377           else if (!info->parsed_tags)
1378             {
1379               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1380                          _("A <text> element can't occur before a <tags> element"));
1381               return;
1382             }
1383
1384           if (!check_no_attributes (context, element_name,
1385                                     attribute_names, attribute_values, error))
1386             return;
1387
1388           push_state (info, STATE_TEXT);
1389           break;
1390         }
1391       else
1392         set_error (error, context,
1393                    G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1394                  _("Element <%s> is not allowed below <%s>"),
1395                    element_name, "text_view_markup");
1396       break;
1397     case STATE_TAGS:
1398       parse_tag_element (context, element_name,
1399                          attribute_names, attribute_values,
1400                          info, error);
1401       break;
1402     case STATE_TAG:
1403       parse_attr_element (context, element_name,
1404                           attribute_names, attribute_values,
1405                           info, error);
1406       break;
1407     case STATE_TEXT:
1408     case STATE_APPLY_TAG:
1409       parse_apply_tag_element (context, element_name,
1410                                attribute_names, attribute_values,
1411                                info, error);
1412       break;
1413     default:
1414       g_assert_not_reached ();
1415       break;
1416     }
1417 }
1418
1419 static gint
1420 sort_tag_prio (TextTagPrio *a,
1421                TextTagPrio *b)
1422 {
1423   if (a->prio < b->prio)
1424     return -1;
1425   else if (a->prio > b->prio)
1426     return 1;
1427   else
1428     return 0;
1429 }
1430
1431 static void
1432 end_element_handler (GMarkupParseContext  *context,
1433                      const gchar          *element_name,
1434                      gpointer              user_data,
1435                      GError              **error)
1436 {
1437   ParseInfo *info = user_data;
1438   gchar *tmp;
1439   GList *list;
1440
1441   switch (peek_state (info))
1442     {
1443     case STATE_TAGS:
1444       pop_state (info);
1445       g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1446
1447       info->parsed_tags = TRUE;
1448
1449       /* Sort list and add the tags */
1450       info->tag_priorities = g_list_sort (info->tag_priorities,
1451                                           (GCompareFunc)sort_tag_prio);
1452       list = info->tag_priorities;
1453       while (list)
1454         {
1455           TextTagPrio *prio = list->data;
1456
1457           if (info->create_tags)
1458             gtk_text_tag_table_add (info->buffer->tag_table, prio->tag);
1459
1460           g_object_unref (prio->tag);
1461           prio->tag = NULL;
1462
1463           list = list->next;
1464         }
1465
1466       break;
1467     case STATE_TAG:
1468       pop_state (info);
1469       g_assert (peek_state (info) == STATE_TAGS);
1470
1471       if (info->current_tag->name)
1472         {
1473           /* Add tag to defined tags hash */
1474           tmp = g_strdup (info->current_tag->name);
1475           g_hash_table_insert (info->defined_tags,
1476                                tmp, tmp);
1477         }
1478       else
1479         {
1480           g_hash_table_insert (info->anonymous_tags,
1481                                GINT_TO_POINTER (info->current_tag_id),
1482                                info->current_tag);
1483         }
1484
1485       if (info->create_tags)
1486         {
1487           TextTagPrio *prio;
1488
1489           /* add the tag to the list */
1490           prio = g_new0 (TextTagPrio, 1);
1491           prio->prio = info->current_tag_prio;
1492           prio->tag = info->current_tag;
1493
1494           info->tag_priorities = g_list_prepend (info->tag_priorities, prio);
1495         }
1496
1497       info->current_tag = NULL;
1498       break;
1499     case STATE_ATTR:
1500       pop_state (info);
1501       g_assert (peek_state (info) == STATE_TAG);
1502       break;
1503     case STATE_APPLY_TAG:
1504       pop_state (info);
1505       g_assert (peek_state (info) == STATE_APPLY_TAG ||
1506                 peek_state (info) == STATE_TEXT);
1507
1508       /* Pop tag */
1509       info->tag_stack = g_slist_delete_link (info->tag_stack,
1510                                              info->tag_stack);
1511
1512       break;
1513     case STATE_TEXT:
1514       pop_state (info);
1515       g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1516
1517       info->spans = g_list_reverse (info->spans);
1518       info->parsed_text = TRUE;
1519       break;
1520     case STATE_TEXT_VIEW_MARKUP:
1521       pop_state (info);
1522       g_assert (peek_state (info) == STATE_START);
1523       break;
1524     case STATE_PIXBUF:
1525       pop_state (info);
1526       g_assert (peek_state (info) == STATE_APPLY_TAG ||
1527                 peek_state (info) == STATE_TEXT);
1528       break;
1529     default:
1530       g_assert_not_reached ();
1531       break;
1532     }
1533 }
1534
1535 static gboolean
1536 all_whitespace (const char *text,
1537                 int         text_len)
1538 {
1539   const char *p;
1540   const char *end;
1541
1542   p = text;
1543   end = text + text_len;
1544
1545   while (p != end)
1546     {
1547       if (!g_ascii_isspace (*p))
1548         return FALSE;
1549
1550       p = g_utf8_next_char (p);
1551     }
1552
1553   return TRUE;
1554 }
1555
1556 static void
1557 text_handler (GMarkupParseContext  *context,
1558               const gchar          *text,
1559               gsize                 text_len,
1560               gpointer              user_data,
1561               GError              **error)
1562 {
1563   ParseInfo *info = user_data;
1564   TextSpan *span;
1565
1566   if (all_whitespace (text, text_len) &&
1567       peek_state (info) != STATE_TEXT &&
1568       peek_state (info) != STATE_APPLY_TAG)
1569     return;
1570
1571   switch (peek_state (info))
1572     {
1573     case STATE_START:
1574       g_assert_not_reached (); /* gmarkup shouldn't do this */
1575       break;
1576     case STATE_TEXT:
1577     case STATE_APPLY_TAG:
1578       if (text_len == 0)
1579         return;
1580
1581       span = g_new0 (TextSpan, 1);
1582       span->text = g_strndup (text, text_len);
1583       span->tags = g_slist_copy (info->tag_stack);
1584
1585       info->spans = g_list_prepend (info->spans, span);
1586       break;
1587     default:
1588       g_assert_not_reached ();
1589       break;
1590     }
1591 }
1592
1593 static void
1594 parse_info_init (ParseInfo     *info,
1595                  GtkTextBuffer *buffer,
1596                  gboolean       create_tags,
1597                  GList         *headers)
1598 {
1599   info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
1600
1601   info->create_tags = create_tags;
1602   info->headers = headers;
1603   info->defined_tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1604   info->substitutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1605   info->anonymous_tags = g_hash_table_new_full (NULL, NULL, NULL, NULL);
1606   info->tag_stack = NULL;
1607   info->spans = NULL;
1608   info->parsed_text = FALSE;
1609   info->parsed_tags = FALSE;
1610   info->current_tag = NULL;
1611   info->current_tag_prio = -1;
1612   info->tag_priorities = NULL;
1613
1614   info->buffer = buffer;
1615 }
1616
1617 static void
1618 text_span_free (TextSpan *span)
1619 {
1620   g_free (span->text);
1621   g_slist_free (span->tags);
1622   g_free (span);
1623 }
1624
1625 static void
1626 parse_info_free (ParseInfo *info)
1627 {
1628   GList *list;
1629
1630   g_slist_free (info->tag_stack);
1631   g_slist_free (info->states);
1632
1633   g_hash_table_destroy (info->substitutions);
1634   g_hash_table_destroy (info->defined_tags);
1635
1636   if (info->current_tag)
1637     g_object_unref (info->current_tag);
1638
1639   list = info->spans;
1640   while (list)
1641     {
1642       text_span_free (list->data);
1643
1644       list = list->next;
1645     }
1646   g_list_free (info->spans);
1647
1648   list = info->tag_priorities;
1649   while (list)
1650     {
1651       TextTagPrio *prio = list->data;
1652
1653       if (prio->tag)
1654         g_object_unref (prio->tag);
1655       g_free (prio);
1656
1657       list = list->next;
1658     }
1659   g_list_free (info->tag_priorities);
1660
1661 }
1662
1663 static void
1664 insert_text (ParseInfo   *info,
1665              GtkTextIter *iter)
1666 {
1667   GtkTextIter start_iter;
1668   GtkTextMark *mark;
1669   GList *tmp;
1670   GSList *tags;
1671
1672   start_iter = *iter;
1673
1674   mark = gtk_text_buffer_create_mark (info->buffer, "deserialize_insert_point",
1675                                       &start_iter, TRUE);
1676
1677   tmp = info->spans;
1678   while (tmp)
1679     {
1680       TextSpan *span = tmp->data;
1681
1682       if (span->text)
1683         gtk_text_buffer_insert (info->buffer, iter, span->text, -1);
1684       else
1685         {
1686           gtk_text_buffer_insert_pixbuf (info->buffer, iter, span->pixbuf);
1687           g_object_unref (span->pixbuf);
1688         }
1689       gtk_text_buffer_get_iter_at_mark (info->buffer, &start_iter, mark);
1690
1691       /* Apply tags */
1692       tags = span->tags;
1693       while (tags)
1694         {
1695           GtkTextTag *tag = tags->data;
1696
1697           gtk_text_buffer_apply_tag (info->buffer, tag,
1698                                      &start_iter, iter);
1699
1700           tags = tags->next;
1701         }
1702
1703       gtk_text_buffer_move_mark (info->buffer, mark, iter);
1704
1705       tmp = tmp->next;
1706     }
1707
1708   gtk_text_buffer_delete_mark (info->buffer, mark);
1709 }
1710
1711
1712
1713 static int
1714 read_int (const guchar *start)
1715 {
1716   int result;
1717
1718   result =
1719     start[0] << 24 |
1720     start[1] << 16 |
1721     start[2] << 8 |
1722     start[3];
1723
1724   return result;
1725 }
1726
1727 static gboolean
1728 header_is (Header      *header,
1729            const gchar *id)
1730 {
1731   return (strncmp (header->id, id, strlen (id)) == 0);
1732 }
1733
1734 static GList *
1735 read_headers (const gchar *start,
1736               gint         len,
1737               GError     **error)
1738 {
1739   int i = 0;
1740   int section_len;
1741   Header *header;
1742   GList *headers = NULL;
1743
1744   while (i < len)
1745     {
1746       if (i + 30 >= len)
1747         goto error;
1748
1749       if (strncmp (start + i, "GTKTEXTBUFFERCONTENTS-0001", 26) == 0 ||
1750           strncmp (start + i, "GTKTEXTBUFFERPIXBDATA-0001", 26) == 0)
1751         {
1752           section_len = read_int ((const guchar *) start + i + 26);
1753
1754           if (i + 30 + section_len > len)
1755             goto error;
1756
1757           header = g_new0 (Header, 1);
1758           header->id = start + i;
1759           header->length = section_len;
1760           header->start = start + i + 30;
1761
1762           i += 30 + section_len;
1763
1764           headers = g_list_prepend (headers, header);
1765         }
1766       else
1767         break;
1768     }
1769
1770   return g_list_reverse (headers);
1771
1772  error:
1773   g_list_foreach (headers, (GFunc) g_free, NULL);
1774   g_list_free (headers);
1775
1776   g_set_error (error,
1777                G_MARKUP_ERROR,
1778                G_MARKUP_ERROR_PARSE,
1779                _("Serialized data is malformed"));
1780
1781   return NULL;
1782 }
1783
1784 static gboolean
1785 deserialize_text (GtkTextBuffer *buffer,
1786                   GtkTextIter   *iter,
1787                   const gchar   *text,
1788                   gint           len,
1789                   gboolean       create_tags,
1790                   GError       **error,
1791                   GList         *headers)
1792 {
1793   GMarkupParseContext *context;
1794   ParseInfo info;
1795   gboolean retval = FALSE;
1796
1797   static const GMarkupParser rich_text_parser = {
1798     start_element_handler,
1799     end_element_handler,
1800     text_handler,
1801     NULL,
1802     NULL
1803   };
1804
1805   parse_info_init (&info, buffer, create_tags, headers);
1806
1807   context = g_markup_parse_context_new (&rich_text_parser,
1808                                         0, &info, NULL);
1809
1810   if (!g_markup_parse_context_parse (context,
1811                                      text,
1812                                      len,
1813                                      error))
1814     goto out;
1815
1816   if (!g_markup_parse_context_end_parse (context, error))
1817     goto out;
1818
1819   retval = TRUE;
1820
1821   /* Now insert the text */
1822   insert_text (&info, iter);
1823
1824  out:
1825   parse_info_free (&info);
1826
1827   g_markup_parse_context_free (context);
1828
1829   return retval;
1830 }
1831
1832 gboolean
1833 _gtk_text_buffer_deserialize_rich_text (GtkTextBuffer *register_buffer,
1834                                         GtkTextBuffer *content_buffer,
1835                                         GtkTextIter   *iter,
1836                                         const guint8  *text,
1837                                         gsize          length,
1838                                         gboolean       create_tags,
1839                                         gpointer       user_data,
1840                                         GError       **error)
1841 {
1842   GList *headers;
1843   Header *header;
1844   gboolean retval;
1845
1846   headers = read_headers ((gchar *) text, length, error);
1847
1848   if (!headers)
1849     return FALSE;
1850
1851   header = headers->data;
1852   if (!header_is (header, "GTKTEXTBUFFERCONTENTS-0001"))
1853     {
1854       g_set_error (error,
1855                    G_MARKUP_ERROR,
1856                    G_MARKUP_ERROR_PARSE,
1857                    _("Serialized data is malformed. First section isn't GTKTEXTBUFFERCONTENTS-0001"));
1858
1859       retval = FALSE;
1860       goto out;
1861     }
1862
1863   retval = deserialize_text (content_buffer, iter,
1864                              header->start, header->length,
1865                              create_tags, error, headers->next);
1866
1867  out:
1868   g_list_foreach (headers, (GFunc)g_free, NULL);
1869   g_list_free (headers);
1870
1871   return retval;
1872 }