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